/**
 * (created at 2010-12-31)
 */
package qiushuo.mysqlparser.recognizer;

import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
 * @author <a href="mailto:QiuShuo1985@gmail.com">QIU Shuo</a>
 */
public class TokenReader {
    public TokenReader(SQLLexer lexer) {
        if (lexer == null) {
            throw new IllegalArgumentException("argument 'lexer' is null!");
        }
        List<SQLToken> list = new LinkedList<SQLToken>();
        for (;;) {
            SQLToken token = lexer.next();
            if (token.isTypeOf(SQLTokenType.EOF)) {
                break;
            }
            list.add(token);
        }
        this.tokenList = list.toArray(new SQLToken[list.size()]);
    }

    /** no null element exist */
    private SQLToken[] tokenList;
    private int nextIndex;
    private int mark;

    public int tokensLeft() {
        return tokenList.length - nextIndex;
    }

    public boolean hasNext() {
        return nextIndex < tokenList.length;
    }

    /**
     * @param left 1 equals to {@link #hasNext()}
     */
    public boolean hasNext(int left) {
        return nextIndex + left <= tokenList.length;
    }

    /**
     * @param offset if 0 compare current token
     */
    public boolean isNext(SQLTokenType type, int offset) {
        int index = nextIndex + offset;
        if (index >= tokenList.length) return false;
        return tokenList[index].isTypeOf(type);
    }

    public boolean isNext(SQLTokenType type, int offset, boolean ignoreCase, String... texts) {
        int index = nextIndex + offset;
        if (index >= tokenList.length) return false;
        if (texts == null || texts.length <= 0) {
            return tokenList[index].getText() == null
                   && ((type == null) ? true : type.equals(tokenList[index].getType()));
        }
        boolean isNull = false;
        Set<String> set = new HashSet<String>(texts.length);
        for (String t : texts) {
            if (t != null) {
                set.add(t);
            } else {
                isNull = true;
            }
        }
        String tme = tokenList[index].getText();
        if (tme == null) return isNull && ((type == null) ? true : type.equals(tokenList[index].getType()));
        if (ignoreCase) {
            return set.contains(tme.toUpperCase()) && ((type == null) ? true : type.equals(tokenList[index].getType()));
        } else {
            return set.contains(tme) && ((type == null) ? true : type.equals(tokenList[index].getType()));
        }

    }

    /**
     * @param texts <b>upper case if ignoreCase is true.</b> If <b>ANY</b> of
     *            texts is matched return true
     * @param offset if 0 compare current token
     */
    public boolean isNext(int offset, boolean ignoreCase, String... texts) {
        return isNext(null, offset, ignoreCase, texts);
    }

    /**
     * @param offset if 0, then return token current index points to
     */
    public SQLToken lookAhead(int offset) {
        int index = nextIndex + offset;
        if (index < 0 || index >= tokenList.length) return null;
        return tokenList[index];
    }

    public int getTotalSize() {
        return tokenList.length;
    }

    public int getCurrentIndex() {
        return nextIndex;
    }

    /**
     * @return <code>null</code> if end of token stream
     */
    public SQLToken next() {
        if (nextIndex >= tokenList.length) {
            return null;
        }
        return tokenList[nextIndex++];
    }

    public void back() {
        if (nextIndex > 0) {
            --nextIndex;
        }
    }

    /**
     * back without check
     */
    public void backForce() {
        --nextIndex;
    }

    /**
     * skip without check
     */
    public void skipForce(int offset) {
        nextIndex += offset;
    }

    //    /**
    //     * @param offset if 0, do nothing
    //     */
    //    public void skip(final int offset) {
    //        switch (offset) {
    //        case -1:
    //            if (nextIndex > 0) {
    //                --nextIndex;
    //            }
    //            return;
    //        case 0:
    //            return;
    //        case 1:
    //            if (nextIndex < tokenList.length) {
    //                ++nextIndex;
    //            }
    //            return;
    //        default:
    //            nextIndex += offset;
    //            if (nextIndex > tokenList.length || nextIndex < 0) {
    //                nextIndex -= offset;
    //            }
    //        }
    //    }

    /**
     * without check
     */
    public void skipTo(int index) {
        nextIndex = index;
    }

    public void mark() {
        mark = nextIndex;
    }

    public void reset() {
        nextIndex = mark;
    }

    @Override
    public String toString() {
        return new StringBuilder().append("TokenReader [nextIndex=")
                                  .append(nextIndex)
                                  .append(", tokenList=")
                                  .append(Arrays.toString(tokenList))
                                  .append("]")
                                  .toString();
    }
}
